package org.gap.jseed;

import java.lang.reflect.Method;

import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException;

public class JavaWriter {

	public static String returnCall(CtMethod method, String body) throws NotFoundException {
		String result = "";
		if (!method.getReturnType().equals(CtClass.voidType)) {
			result = PrimitiveJavaWriter.writePrimitiveSaveReturn(method.getReturnType(), body);
		} else {
			result = line(body);
		}
		return result;
	}
	
	
	public static String getParameterTypes(final CtClass[] parameterTypes) {
		final StringBuffer result = new StringBuffer();
		if (parameterTypes.length == 0) {
			return "new Class[0]";
		}
		for (final CtClass each : parameterTypes) {
			result.append(each.getName() + ".class, ");
		}
		trimTrailingComma(result);
		
		return "new Class[] {" + result + "}";
	}
	
	public static String asString(String name) {
		return "\"" + name + "\"";
	}

	public static String call(String method, String onObject, String...withParameters) {
		StringBuffer parameters = new StringBuffer();
		for (String each : withParameters) {
			parameters.append(each + ", ");
		}
		if (withParameters.length > 0) {
			parameters.setLength(parameters.length() - 2);
		}
		return onObject + "." + method + "(" + parameters + ")";
	}
	
	public static Object tryCatch(String message, String...statements) {
		StringBuffer body = new StringBuffer();
		for (String each : statements) {
			body.append(line(each));
		}
		return "\ntry {\n" + indent(body.toString()) + "} " +
				"catch(Exception e) { \n" +
				"    e.printStackTrace(); \n" +
				"    throw new RuntimeException(\"" + message + "\", e); \n" +
				"}\n";
	}

	public static String getField(String name, CtField field) {
		return assignTo("field", name + ".class.getDeclaredField(" + asString(field.getName()) + ")");
	}
	
	public static String createParameters(final int length) {
		final StringBuffer result = new StringBuffer();
		if (length == 0) {
			return "new Object[0]";
		}
		for (int i = 1; i <= length; i++) {
			result.append("($w)$" + i + ", ");
		}
		trimTrailingComma(result);
		return "new Object[] {" + result + "}";
	}
	
	public static String line(String statement) {
		return statement + ";\n";
	}
	
	public static String assignTo(String variable, String value) {
		return variable + " = " + value;
	}
	
	public static String instantiate(Class<?> type) {
		return "new " + type.getName() + "()";
	}
	
	public static String braces(String body) {
		return "{\n" + indent(body) + "}\n";
	}
	
	private static String indent(String body) {
		StringBuffer result = new StringBuffer();
		for (String each : body.split("\n")) {
			if (isEmptyLine(each)) { continue; }
			result.append("    " + each + "\n");
		}
		return result.toString();
	}

	private static boolean isEmptyLine(String each) {
		return each.trim().isEmpty() || each.trim().equals(";");
	}

	public static String getInvokingMethodCode(CtMethod each, String parameterTypes) {
		return  " " + Method.class.getName() + " method = null; \n" +
			tryCatch("invoking method failed", 
					"method = this.getClass().getDeclaredMethod(" + asString(each.getName()) + ", " + parameterTypes + ")"
			);
	}
	
	/**
	 * Ensures the a proper default value is created.
	 * 
	 * @param returnType
	 * @return
	 * @throws NotFoundException 
	 */
	public static String defaultValue(CtClass returnType) throws NotFoundException {
		return PrimitiveJavaWriter.defaultValue(returnType);
	}
	
	public static String semicolon() {
		return ";\n";
	}
	
	private static void trimTrailingComma(final StringBuffer result) {
		if (!result.toString().isEmpty()) {
			result.setLength(result.length() - 2);
		}
	}
}
